package com.atguigu.tingshu.user.service.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.TrackClientFeign;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.VipServiceConfigService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigDecimal;
import java.security.interfaces.RSAPrivateKey;
import java.util.*;

/***
 * 登录:
 * 1. 合法性校验-
 * 2. token过期续期
 */
@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Value("${wechat.login.appId}")
    private String appId;
    @Value("${wechat.login.appSecret}")
    private String appSecret;
    @Autowired
    private RSAPrivateKey rsaPrivateKey;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TrackClientFeign trackClientFeign;
    @Autowired
    private UserCollectTableMapper userCollectTableMapper;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 登录发放令牌(token)
     *
     * @param code
     * @return
     */
    @Override
    public Object wxLogin(String code) {
        // 校验参数 -- 微信返回的code
        if (StringUtils.isEmpty(code)) {
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        // 从微信服务器获取当前登录用户的openid
        // https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
        // grant_type=authorization_code
        String url = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
        // appid=APPID
        url += "&appid=" + appId;
        // secret=SECRET
        url += "&secret=" + appSecret;
        // js_code=JSCODE
        url += "&js_code=" + code;
        // 发起get请求
        ResponseEntity<String> exchange =
                restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        // 获取结果
        String result = exchange.getBody();
        // 反序列化
        JSONObject resultJson =
                JSONObject.parseObject(result, JSONObject.class);
        // 获取openid
        String openid = resultJson.get("openid").toString();

        // 获取设备号：登录请求的设备号(唯一标识码--明文)--存储(不能暴露)
        // 例: 1.记录到MySQL(下次登录就会校验，每次使用都校验) -- 安全 2. redis(缓存)登录的时候记录，使用的 时候校验 -- 效率
        // 记录设备号:模拟(实际由前端传递,一般放在header中)
        String mNum = "123456";
        // 新建加密器
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        mNum = bCryptPasswordEncoder.encode(mNum);
        // 判断用户是否为第一次登录
        UserInfo userInfo =
                getOne(new LambdaQueryWrapper<UserInfo>()
                        .eq(UserInfo::getWxOpenId, openid));
        // 若userInfo为null，初始化用户信息
        if (userInfo == null) {
            userInfo = new UserInfo();
            userInfo.setNickname("新用户测试");
            userInfo.setAvatarUrl("http://192.168.200.130:9000/atguiguts/71e9276f638d49f2ba7c9d8c8b845e4f.jpg");
            userInfo.setWxOpenId(openid);
            userInfo.setPassword(mNum);
            userInfo.setVipExpireTime(new Date());
            save(userInfo);
            // 账户初始化 account
            UserAccount userAccount = new UserAccount();
            userAccount.setUserId(userInfo.getId());
            // 发消息通知service-account写库
            rabbitTemplate.convertAndSend("account_init_exchange","account.init",userInfo.getId() + "");
        } else {
            // 非初次登录:更新设备
            userInfo.setPassword(mNum);
            updateById(userInfo);
            // TODO-通知所有相关的服务完成数据预热
        }
        // 生成jwt令牌,前端类型为map，包装成map后返回
        Map map = new HashMap<>();
        map.put("userId", userInfo.getId());
        // 记录用户的登录时间: 令牌的失效时间
        map.put("time", System.currentTimeMillis());
        // 储存VIP状态 0-是 1-不是
        map.put("isVip", userInfo.getIsVip());
        // 存储VIP过期时间
        map.put("vipExpireTime", userInfo.getVipExpireTime().getTime());
        Jwt encode = JwtHelper.encode(JSONObject.toJSONString(map), new RsaSigner(rsaPrivateKey));
        String token = encode.getEncoded();
        redisTemplate.opsForHash().put("token", (userInfo.getId()).toString(), (token).toString());
        map = new HashMap();
        map.put("token", token);
        return map;
    }

    /**
     * 刷新令牌
     *
     * @param mNum
     * @return
     */
    @Override
    public Map getNewToken(String mNum) {
        // 获取Request
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        // 获取旧令牌
        String token = request.getHeader("token");
        // 校验
        if (StringUtils.isEmpty(token)) {
            // 无令牌,转去登录页面
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        try {
            // 校验旧令牌
            Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(SystemConstant.PUBLIC_KEY));
            // 获取载荷
            String claims = jwt.getClaims();
            // 反序列化
            JSONObject parseObject = JSONObject.parseObject(claims, JSONObject.class);
            // 获取用户id
            String userId = parseObject.get("userId").toString();
            // 根据用户id查询设备号
            UserInfo userInfo = getById(Long.valueOf(userId));
            // 创建加密器对象
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            // 校验设备号明文 & 密文
            if (bCryptPasswordEncoder.matches(mNum, userInfo.getPassword())) {
                // 若设备相同
                // 判断是否需要强制失效(条件:令牌已刷新)
                String tokenFromRedis = redisTemplate.opsForHash().get("token", (userId).toString()).toString();
                if (tokenFromRedis.equals(token)) {
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                } else {
                    redisTemplate.opsForHash().put("token", (userId).toString(), (token).toString());
                }
                // 发放新令牌
                Map map = new HashMap<>();
                map.put("userId", userId);
                map.put("time", System.currentTimeMillis());
                Jwt encode = JwtHelper.encode(JSONObject.toJSONString(map), new RsaSigner(rsaPrivateKey));
                token = encode.getEncoded();
                map.put("token", token);
                return map;
            }

        } catch (Exception e) {
            // 令牌校验不通过,转去登录页面
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
    }

    /**
     * 登录成功后获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserInfoVo getUserInfoVo(long userId) {
        UserInfoVo userInfoVo = new UserInfoVo();
        UserInfo userInfo = getById(userId);
        BeanUtils.copyProperties(userInfo, userInfoVo);
        return userInfoVo;
    }

    /**
     * 用户收藏 -- MongoDB
     *
     * @param trackId
     */
    @Override
    public void collect(Long trackId) {
        // 验证声音是否纯在
        if (trackClientFeign.getTrackInfo(trackId)) {
            // 获取userId
            Long userId = AuthContextHolder.getUserId();
            // 拼接collectName
            String collectName = "userCollect_" + trackId;
            // 初始化
            UserCollect userCollect = new UserCollect();
            userCollect.setUserId(userId);
            userCollect.setTrackId(trackId);
            // 存入mongo
            mongoTemplate.save(userCollect, collectName);
        }
    }

    /**
     * 用户取消收藏 - MongoDB
     *
     * @param trackId
     */
    @Override
    public void cancelCollect(Long trackId) {
        // 获取userId
        Long userId = AuthContextHolder.getUserId();
        // 拼接collectName
        String collectName = "userCollect_" + trackId;
        // 从MongoDB中删除
        mongoTemplate.remove(
                new Query().addCriteria(Criteria
                        .where("userId").is(userId)
                        .and("trackId").is(trackId)),
                collectName);
    }

    /**
     * 判断用户是否收藏了该声音 - MongoDB
     *
     * @param trackId
     * @return
     */
    @Override
    public Object isCollect(Long trackId) {
        // 获取userId
        Long userId = AuthContextHolder.getUserId();
        // 拼接collectName
        String collectName = "userCollect_" + trackId;
        // 从mongo查询并返回结果
        return mongoTemplate.findOne(
                new Query().addCriteria(Criteria
                        .where("userId").is(userId)
                        .and("trackId").is(trackId)),
                UserCollect.class,
                collectName)
                != null;
    }


    /**
     * MySQL
     * 用户收藏
     *
     * @param trackId
     * @return
     */
    @Override
    public void collectMysql(Long trackId) {
        // 验证声音是否存在
        if (trackClientFeign.getTrackInfo(trackId)) {
            // 获取用户id
            Long userId = AuthContextHolder.getUserId();
            // 删除旧的收藏数据
            userCollectTableMapper.delete(
                    new LambdaQueryWrapper<UserCollectTable>()
                            .eq(UserCollectTable::getUserId, userId)
                            .eq(UserCollectTable::getTrackId, trackId));
            // 初始化收藏实体
            UserCollectTable userCollectTable = new UserCollectTable();
            // 属性赋值：哪个用户userId 收藏的哪个声音trackId
            userCollectTable.setUserId(userId);
            userCollectTable.setTrackId(trackId);
            // 存入MySQL
            userCollectTableMapper.insert(userCollectTable);
        }
    }

    /**
     * MySQL
     * 用户取消收藏
     *
     * @param trackId
     * @return
     */
    @Override
    public void cancelCollectMysql(Long trackId) {
        // 获取当前用户信息
        Long userId = AuthContextHolder.getUserId();
        // 从MySQL中删除该用户的该声音收藏
        userCollectTableMapper.delete(
                new LambdaQueryWrapper<UserCollectTable>()
                        .eq(UserCollectTable::getUserId, userId)
                        .eq(UserCollectTable::getTrackId, trackId));
    }

    /**
     * MySQL
     * 查询用户是否收藏了当前声音
     *
     * @param trackId
     * @return
     */
    @Override
    public Object isCollectMysql(Long trackId) {
        // 获取当前用户信息
        Long userId = AuthContextHolder.getUserId();
        return userCollectTableMapper.selectOne(
                new LambdaQueryWrapper<UserCollectTable>()
                        .eq(UserCollectTable::getUserId, userId)
                        .eq(UserCollectTable::getTrackId, trackId))
                != null;
    }

    @Resource
    private OrderInfoFeignClient orderInfoFeignClient;

    /**
     * 记录用户消费行为
     * @param orderNo
     */
    @Override
    public void addUserPaid(String orderNo) {
        // 查询订单信息
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderNo);
        // 查询订单类型
        String itemType = orderInfo.getItemType();
        // 根据类型更新对应表格
        switch (itemType){
            case "1001" -> addAlbumPaidInfo(orderInfo);
            case "1002" -> addTrackPaidInfo(orderInfo);
            case "1003" -> addVipPaidInfo(orderInfo);
        }
    }

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;
    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    /**
     * 记录用户 会员购买 记录
     * @param orderInfo
     */
    private void addVipPaidInfo(OrderInfo orderInfo) {
        // 查询订单购买的vip类型
        VipServiceConfig vipServiceConfig =
                vipServiceConfigMapper.selectById(orderInfo.getOrderDetailList().get(0).getItemId());
        // 获取服务时长
        Integer serviceMonth = vipServiceConfig.getServiceMonth();
        // 查询用户信息
        UserInfo userInfo = getById(orderInfo.getUserId());
        // 日历初始化 - 记录服务开始时间
        Calendar calendar = Calendar.getInstance();
        if (userInfo.getIsVip() == 0 ){
            // 所购服务开始时间 = 当前时间
            calendar.setTime(new Date());
            userInfo.setIsVip(1);
        }else {
            // 所购服务开始时间 = 用户当前vip结束时间
            calendar.setTime(userInfo.getVipExpireTime());
        }
        // 计算服务结束时间 --> 服务开始时间 + 新购服务时长
        calendar.add(Calendar.MONTH,serviceMonth);
        // 初始化服务实体
        UserVipService userVipService = new UserVipService();
        // 属性赋值
        userVipService.setOrderNo(orderInfo.getOrderNo());
        userVipService.setUserId(orderInfo.getUserId());
        userVipService.setStartTime(new Date());
        userVipService.setExpireTime(calendar.getTime());
        // 保存
        userVipServiceMapper.insert(userVipService);
        // 更新用户vip过期时间
        userInfo.setVipExpireTime(userVipService.getExpireTime());
        // 更新用户信息
        updateById(userInfo);
    }
    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;
    /**
     * 记录用户 声音购买 记录
     * @param orderInfo
     */
    private void addTrackPaidInfo(OrderInfo orderInfo) {
        // 获取trackId
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        Long trackId = orderDetailList.get(0).getItemId();
        // 获取trackInfo实体 --> 获取albumId
        TrackInfo trackInfo = trackClientFeign.getTrackInfoDetail(trackId);
        // 遍历添加用户又没声音数据
        orderDetailList.stream().forEach(orderDetail -> {
            // 初始化
            UserPaidTrack userPaidTrack = new UserPaidTrack();
            // 属性赋值
            userPaidTrack.setOrderNo(orderInfo.getOrderNo());
            userPaidTrack.setUserId(orderInfo.getUserId());
            userPaidTrack.setAlbumId(trackInfo.getAlbumId());
            userPaidTrack.setTrackId(orderDetail.getItemId());
            // 保存
            userPaidTrackMapper.insert(userPaidTrack);
        });
    }

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;
    /**
     * 记录用户 专辑购买 记录
     * @param orderInfo
     */
    private void addAlbumPaidInfo(OrderInfo orderInfo) {
        // 初始化
        UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
        // 赋值
        userPaidAlbum.setOrderNo(orderInfo.getOrderNo());
        userPaidAlbum.setUserId(orderInfo.getUserId());
        userPaidAlbum.setAlbumId(orderInfo.getOrderDetailList().get(0).getItemId());
        // 保存
        userPaidAlbumMapper.insert(userPaidAlbum);
    }
}
